#!/usr/bin/env python3

## demure's fancy led matrix clock -- python sample
## This is a fancy led matrix clock written for a pi with an led matrix and
## and ada fruit matrix hat.

## NOTE: This python version is mostly a test, and the C version is the primary code.
##       This is due to the python being pretty slow, especially on a pi zero.


## TODO:
## * Wrap weather request in `try` and supply fail case json sample
## * Get RGBMatrixOptions working
## * Remove object oriented junk?
## * Rename default instance
## * Ensure weather icons match nightmode
##
## * add wakeup mode? like not super bright before 0800 weekday, 1000 weekend
## * make config.json and move openweather api_key/city_id. maybe have nightmode time conf


from samplebase import SampleBase
from rgbmatrix import graphics
from rgbmatrix import RGBMatrix, RGBMatrixOptions
import time
# from datetime import date
from datetime import datetime
import requests, json
from pathlib import Path

from PIL import Image
from PIL import ImageDraw


## Configuration for the matrix
options = RGBMatrixOptions()
options.rows = 64
options.cols = 64
options.chain_length = 1
options.parallel = 1
options.hardware_mapping = 'adafruit-hat'
options.gpio_slowdown = 4

# matrix = RGBMatrix(options=options)


## Prep Dirs
base_dir = Path(__file__).parent
font_dir = str(base_dir) + '/spleen/'
icon_dir = str(base_dir) + '/weathericons/'


## Load config.json
with open(base_dir / "config.json") as f:
    try:
        conf = json.load(f)
    except:
        print("Error: invalid json in config.json")
        quit()

base_url = conf["base_url"]
api_key  = conf["api_key"]
city_id  = conf["city_id"]

global complete_url
complete_url = base_url + "appid=" + api_key + "&id=" + city_id
# x = json.loads('{"weather": [{"main": "NULL", "description": "NULL", "icon": "NULL"}], "main": {"temp": 0, "feels_like": 0, "temp_min": 0, "temp_max": 0, "pressure": 0, "humidity": 0}, "clouds": {"all": 0}, "dt": 0, "sys": {"sunrise": 0, "sunset": 0}, "cod": 0}')


### Night Mode Check Function ### {{{
def night_mode_check(now, c_up, c_sunrise, c_sunset):
    now_s = int(now.strftime("%s"))
    hour  = int(now.strftime("%H"))

    ## Set night mode if after sunset/before sunrise,
    ## as long as last weather pull is less than 6 hours old.
    ## Fallback of after 2200 and before 0700.
    if (c_sunset != 0 and (now_s - c_up) < 21600):
        if (now_s > c_sunset or now_s < c_sunrise):
            night_mode = True
        else:
            night_mode = False
    else:
        if (hour >= 22 or hour < 7):
            night_mode = True
        else:
            night_mode = False

    return night_mode
### End Night Mode Check Function ### }}}


### Wakeup Mode Check Function ### {{{
def wakeup_mode_check(now, c_up):
    now_s = int(now.strftime("%s"))
    hour  = int(now.strftime("%H"))
    time  = int(now.strftime("%H%M"))
    day   = int(now.strftime("%w"))   ## return 0 thru 6; 0 = sun, 6 = sat

    ## Weekday wakeup ends at 0800
    ## Weekend wakeup ends at 1000

    if (day == 0 or day == 6):
        ## four hours (6 to 10) = 14400 sec
        wake_s = int(datetime.fromisoformat(now.strftime('%Y-%m-%d 10:00:00')).strftime('%s'))
        # if (hour < 10):
        if (time < 1300):
            wake_math = (1 - ((wake_s - now_s) / 14400))
            wake_mode = round(wake_math, 2)
        else:
            wake_mode = 1
    else:
        ## two hours (6 to 8) = 7200 sec
        wake_s = int(datetime.fromisoformat(now.strftime('%Y-%m-%d 08:00:00')).strftime('%s'))
        if (hour < 22):
            wake_math = (1 - ((wake_s - now_s) / 7200))
            wake_mode = round(wake_math, 2)
        else:
            wake_mode = 1

        ## Sanitize in case we get a negative value
        if (wake_mode < 0):
            wake_mode = 1
    return wake_mode
### End Wakeup Mode Check Function ### }}}


### Wakeup Mode Color Function ### {{{
def wakeup_mode_color(wake_mode):
    temp_red = int((125 * wake_mode) + 25)
    temp_green = int(75 * wake_mode)

    tc_wake = graphics.Color(temp_red, temp_green, 0)

    return tc_wake
### End Wakeup Mode Color Function ### }}}


def drawimage(canvas, path, x, y):
    image = Image.open(path).convert('RGB')
    image.load()
    # matrix.SetImage(image, x, y)
    canvas.SetImage(image, x, y)


class RunText(SampleBase):
    def __init__(self, *args, **kwargs):
        super(RunText, self).__init__(*args, **kwargs)
        # self.parser.add_argument("-t", "--text", help="The text to scroll on the RGB LED panel", default="Hello world!")


    def run(self):
        canvas = self.matrix.CreateFrameCanvas()

        ### Set Fonts ### {{{
        ft_sp_12x24 = graphics.Font()
        ft_sp_12x24.LoadFont(font_dir + 'spleen-12x24.bdf')
        ft_sp_5x8 = graphics.Font()
        ft_sp_5x8.LoadFont(font_dir   + 'spleen-5x8.bdf')
        ft_sp_16x32 = graphics.Font()
        ft_sp_16x32.LoadFont(font_dir + 'spleen-16x32.bdf')
        ft_sp_8x16 = graphics.Font()
        ft_sp_8x16.LoadFont(font_dir  + 'spleen-8x16.bdf')
        ### End Set Fonts ### }}}


        ## Initialize before first weather fetch
        x = json.loads('{"cod": 0}')
        c_up = int(datetime.now().strftime("%s")) - 840     ## Off set to reduce initial weather load delay.
        # c_up = int(datetime.now().strftime("%s")) - 890     ## DEBUG
        c_sunrise = 0
        c_sunset = 0

        while True:
            canvas.Clear()

            ## Initialize Time Vars
            now    = datetime.now()
            now_s  = int(now.strftime("%s"))
            utcnow = datetime.utcnow()
            hour   = int(now.strftime("%H"))


            ### Pull Weather Info ### {{{
            # if (int(now.strftime("%s")) - int(c_up)) > 10:    ## for DEBUG
            if (now_s - c_up) > 900:    ## Refresh every 15min
                graphics.DrawText(canvas, ft_sp_5x8, 0, 12, graphics.Color(0,35,0),    "u")     ## Print update indicator

                ## Request Weather
                try:
                    response = requests.get(complete_url)
                    # response = requests.get("http://test.lan")    ##DEBUG
                    x = response.json()
                except:
                    print("weather fail")   ##DEBUG
                    x = json.loads('{"weather": [{"main": "NULL", "description": "NULL", "icon": "NULL"}], "main": {"temp": 0, "feels_like": 0, "temp_min": 0, "temp_max": 0, "pressure": 0, "humidity": 0}, "clouds": {"all": 0}, "dt": 0, "sys": {"sunrise": 0, "sunset": 0}, "cod": 0}')

                # x["dt"] = now_s     ##DEBUG


                if x["cod"] != 0 and x["cod"] != 404:

                    c_temp    = x["main"]["temp"]
                    c_temp_f  = round((c_temp - 273.15) * 9/5 + 32)
                    c_humid   = x["main"]["humidity"]

                    c_icon    = x["weather"][0]["icon"]
                    c_cond    = x["weather"][0]["id"]
                    # c_desc    = x["weather"][0]["description"]
                    c_sunrise = int(x["sys"]["sunrise"])
                    c_sunset  = int(x["sys"]["sunset"])
                    c_up      = int(x["dt"])
                else:
                    ## Stop weather pulls until normal delay has occured.
                    c_up = now_s
            ### End Pull Weather Info ### }}}


            ## Test for night mode
            night_mode = night_mode_check(now, c_up, c_sunrise, c_sunset)

            ## Test for wakeup mode
            if(night_mode == False):
                wake_mode = wakeup_mode_check(now, c_up)
            else:
                wake_mode = 1


            ### Text Colors ### {{{
            ## Set Day/Night Colors
            if (night_mode == True):
                tc_night = graphics.Color(25, 0, 0)

                tc_time  = tc_night
                tc_week  = tc_night
                tc_date  = tc_night
                tc_utc   = tc_night
                tc_epoch = tc_night
                tc_temp  = tc_night
                tc_humid = tc_night
                tc_sun   = tc_night
            elif (wake_mode < 1):
                tc_wake  = wakeup_mode_color(wake_mode)

                tc_time  = tc_wake
                tc_week  = tc_wake
                tc_date  = tc_wake
                tc_utc   = tc_wake
                tc_epoch = tc_wake
                tc_temp  = tc_wake
                tc_humid = tc_wake
                tc_sun   = tc_wake
            else:
                tc_time  = graphics.Color(0, 0, 200)
                tc_week  = graphics.Color(0, 125, 125)
                tc_date  = graphics.Color(125, 0, 125)
                tc_utc   = graphics.Color(150, 0, 0)
                tc_epoch = graphics.Color(0, 150, 0)
                tc_temp  = graphics.Color(125, 125, 125)
                tc_humid = graphics.Color(125, 125, 0)
                tc_sun   = graphics.Color(150, 75, 0)
            ### Text Colors ### }}}


            ### Draw Day/Night/Late-Night Modes ### {{{
            ## Late-Night Output
            if (hour <= 6):
            # if True:    ## DEBUG
                graphics.DrawText(canvas, ft_sp_16x32,  0, 20, tc_time,     now.strftime("%H%M"))
                graphics.DrawText(canvas, ft_sp_16x32,  0, 42, tc_week,     now.strftime("%a"))

                ## Day Display Weather Info
                if x["cod"] != 0 and x["cod"] != 404:
                    graphics.DrawText(canvas, ft_sp_16x32, 0, 64, tc_temp,   str(c_temp_f) + "F")

            ## Day/Night Output
            else:
                ##                where,  font, Y, X,  color,      string
                graphics.DrawText(canvas, ft_sp_12x24,  2, 15, tc_time,     now.strftime("%R"))
                graphics.DrawText(canvas, ft_sp_5x8,    0, 43, tc_week,     now.strftime("%a, %b"))
                graphics.DrawText(canvas, ft_sp_5x8,    0, 50, tc_date,     now.strftime("%F"))
                graphics.DrawText(canvas, ft_sp_5x8,    0, 57, tc_utc,      utcnow.strftime("%R UTC"))
                graphics.DrawText(canvas, ft_sp_5x8,    0, 64, tc_epoch,    now.strftime("%s"))

                ## Day Display Weather Info
                if x["cod"] != 0 and x["cod"] != 404:
                    graphics.DrawText(canvas, ft_sp_8x16,   0, 27, tc_temp,     str(c_temp_f) + "F")
                    graphics.DrawText(canvas, ft_sp_5x8,    0, 35, tc_humid,    str(c_humid) + "%H")

                    ## Either sunrise or sunset time
                    if (now_s < c_sunrise):
                        graphics.DrawText(canvas, ft_sp_5x8, 25, 35, tc_sun, str(datetime.fromtimestamp(c_sunrise).strftime("%H%M")) + "*")
                    elif (now_s < c_sunset):
                        graphics.DrawText(canvas, ft_sp_5x8, 25, 35, tc_sun, str(datetime.fromtimestamp(c_sunset).strftime("%H%M")) + "*")


                else:
                    pass
                    ##TODO: handle error by displaying something?
                    # graphics.DrawText(canvas, ft_sp_5x8, 0, 36, tc_temp,   "fail")
            ### End Draw Day/Night/Late-Night Modes ### }}}


            ## Display weather icon if not late night mode
            if x["cod"] != 0 and x["cod"] != 404:
                # if night_mode == False:
                if not (hour <= 6) or not (wake_mode == 1):
                    if c_cond == 900:
                        drawimage(canvas, icon_dir + 'tornado' + '.png', 49, 17)
                    elif c_cond == 901 or c_cond == 902:
                        drawimage(canvas, icon_dir + 'hurricane' + '.png', 49, 17)
                    elif c_cond == 906 or c_cond == 611 or c_cond == 612:
                        drawimage(canvas, icon_dir + 'hail' + '.png', 49, 17)
                    elif c_cond == 600 or c_cond == 601 or c_cond == 602:
                        drawimage(canvas, icon_dir + 'snow' + '.png', 49, 17)
                    else:
                        drawimage(canvas, icon_dir + c_icon + '.png', 49, 17)

            # time.sleep(0.05)
            time.sleep(0.1)
            # time.sleep(0.5)
            canvas = self.matrix.SwapOnVSync(canvas)


# Main function
if __name__ == "__main__":
    run_text = RunText()
    if (not run_text.process()):
        run_text.print_help()
